package com.util;

import com.util.annotation.Column;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.sql.*;
import java.time.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * @Author Cc
 * @Date 2024-03-07
 */
public class Dbutil {
    private static String dbDriver;
    private static String dbUrl;
    private static String dbUser;
    private static String dbPwd;
    // 三大对象
    private Connection conn;
    private PreparedStatement psmt;
    private ResultSet rs;

    static {
        InputStream in = Dbutil.class.getResourceAsStream("/jdbc.properties");

        Properties pro = new Properties();

        try {
            pro.load(in);

            dbDriver = pro.getProperty("jdbc.dbDriver");
            dbUrl = pro.getProperty("jdbc.dbUrl");
            dbUser = pro.getProperty("jdbc.dbUser");
            dbPwd = pro.getProperty("jdbc.dbPwd");

            in.close();
        } catch (IOException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
    }

    // 加载驱动
    static {
        try {
            java.lang.Class.forName(dbDriver);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    /**
     * 获取连接
     * @return 返回连接对象
     */
    private Connection getConnection() {
        try {
            conn = DriverManager.getConnection(dbUrl, dbUser, dbPwd);
            return conn;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 增删改的通用方法
     *
     * @param sql
     * @param params
     * @return
     * @throws SQLException
     */
    public int executeUpdate(String sql, Object... params) throws SQLException {
        conn = getConnection(); // 获取连接
        psmt = conn.prepareStatement(sql);
        if (params != null) {
            for (int i = 0; i < params.length; i++) {
                psmt.setObject(i + 1, params[i]); // 设置参数值
            }
        }
        int rows = psmt.executeUpdate(); // 执行sql
        // 关闭对象
        closeAll();
        return rows;
    }

    public int executeUpdate(StringBuilder sql, List<Object> params) throws SQLException {
        conn = getConnection(); // 获取连接
        psmt = conn.prepareStatement(sql.toString());
        if (params != null) {
            for (int i = 0; i < params.size(); i++) {
                psmt.setObject(i + 1, params.get(i)); // 设置参数值
            }
        }
        int rows = psmt.executeUpdate(); // 执行sql
        // 关闭对象
        closeAll();
        return rows;
    }

    /**
     * 查询的通用方法
     * @param sql
     * @param params
     * @return
     * @throws SQLException
     */
    public ResultSet executeQuery(String sql, Object... params) throws SQLException {
        conn = getConnection(); // 获取连接
        psmt = conn.prepareStatement(sql);
        if (params != null) {
            for (int i = 0; i < params.length; i++) {
                psmt.setObject(i + 1, params[i]); // 设置参数值
            }
        }
        rs = psmt.executeQuery(); // 执行查询
        return rs;
    }
    /**
     * 返回实体对象的属性名称(类映射返回字段名)
     *
     * @param clazz       实体对象的 Class 对象
     * @param columnLabel select 后面的名称(也就是字段名),根据这个返回实体对象要什么字段。
     * @param <T>
     * @return
     */
    private <T> String getFieldName(Class<T> clazz, String columnLabel) {
        // 1.获取实体对象所有的字段对象
        Field[] fields = clazz.getDeclaredFields();

        // 2.循环遍历字段数组
        for (Field field : fields) {
            // 3.判断字段是否存在 Column 注解
            boolean isExist = field.isAnnotationPresent(Column.class);

            // 4.如果存在，则获取注解的内容
            if (isExist) {
                String columnName = field.getAnnotation(Column.class).value();

                // 5.判断注解的内容是否与 select 后面的字段名称一样
                if (columnName.equals(columnLabel)) {
                    // 6.如果一样，则返回字段名称 - staPos
                    return field.getName();
                }
            }
        }
        // 7.如果不一样，则返回 select 后面的字段名称
        return columnLabel;
    }
    /**
     * 通用查询操作 - 返回的List集合
     *
     * @param clazz  实体对象的Class对象
     * @param sql    要执行查询语句
     * @param params 查询语句点位符数据
     * @param <T>    T为具体的实体类型对象
     * @return 返回的List集合
     */
    public <T> List<T> executeQuery(Class<T> clazz, String sql, Object... params) {
        List<T> list = new ArrayList<>();
        conn = getConnection();
        //1、获取连接
        if (conn == null) {
            throw new RuntimeException("Connection为null");
        }

        try {
            psmt = conn.prepareStatement(sql);//2、预编译SQl语句
            if (params != null) {
                for (int i = 0; i < params.length; i++) {
                    psmt.setObject(i + 1, params[i]); // 设置参数值
                }
            }//3、填充sql
            rs = psmt.executeQuery();//4、执行sql,返回resultSet
            ResultSetMetaData metaData = rs.getMetaData();//5、获取结果集元数据对象
            int count = metaData.getColumnCount();//6、查询字段的数量
            while (rs.next()) {
                //1、实例化实体类对象。实体类就是你传入的Class<T> clazz
                T entity = clazz.getConstructor().newInstance();

                //2、读取结果集的各列的数据。
                for (int i = 1; i <= count; i++) {
                    // 2.1）获取查询字段名称 - 必须和实体对象的属性名称保持一致 - sta_pos、add_time
                    String name = getFieldName(clazz, metaData.getColumnLabel(i));

                    // 2.2）根据名称获取实体对象的字段对象
                    Field declaredField = clazz.getDeclaredField(name);

                    // 2.3）设置字段访问权限
                    declaredField.setAccessible(true);

                    // 2.4）获取结果集中的数据
                    Object obj = rs.getObject(i);
                    // 2.5）封装数据到实体对象中 - 思考：获取数据后，给对象的哪个属性初始化呢？ - 反射
                    if (obj != null) {
                        if (obj instanceof Timestamp) {//判断在数据库中是否是DateTime类型
                            Timestamp timestamp = (Timestamp) obj;
                            // 将Timestamp类型转换为java.util.Date类型

                            declaredField.set(entity, timestamp.getTime());

                        } else if (obj instanceof Date) {
                            declaredField.set(entity, obj);
                        } else if (obj instanceof LocalDateTime){
//                            LocalDateTime localDateTime = (LocalDateTime) obj;
//                            ZoneId zoneId = ZoneId.systemDefault();
//                            ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId);
//                            Instant instant = zonedDateTime.toInstant();
//                            java.util.Date date = Date.from(instant);
//                            declaredField.set(entity, date);
                              ZoneId zoneId = ZoneId.systemDefault();
                              Instant instant = ((LocalDateTime) obj).atZone(zoneId).toInstant();
                              Timestamp timestamp = Timestamp.from(instant);
                              declaredField.set(entity,timestamp);
                        }else{
                            declaredField.set(entity, obj);
                        }

                    } else {
                        // 处理null值的逻辑，例如给字段设置一个默认值或者抛出异常
                    }
                }
                // 3.把实体对象添加到 List 集合中
                list.add(entity);
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            closeAll();
        }
        return list;
    }

    public ResultSet executeQuery(StringBuilder sql, List<Object> params) throws SQLException {
        conn = getConnection(); // 获取连接
        psmt = conn.prepareStatement(sql.toString());
        if (params != null) {
            for (int i = 0; i < params.size(); i++) {
                psmt.setObject(i + 1, params.get(i)); // 设置参数值
            }
        }
        rs = psmt.executeQuery(); // 执行查询
        return rs;
    }
    /**
     * 关闭对象的通用方法
     */
    public void closeAll() {
        try {
            if (rs != null) {
                rs.close();
            }
            if (psmt != null) {
                psmt.close();
            }
            if (conn != null) {
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Connection conn = new Dbutil().getConnection();
        System.out.println(conn);
    }
}
